﻿

using System;
using System.Collections;
using System.Collections.Generic;
using Godot;

/// <summary>
/// Ui网格组件
/// </summary>
/// <typeparam name="TUiCellNode">Ui节点类型</typeparam>
/// <typeparam name="TData">传给Cell的数据类型</typeparam>
public class UiGrid<TUiCellNode, TData> : IUiGrid where TUiCellNode : IUiCellNode
{
    /// <summary>
    /// 选中Cell的时的回调, 参数为 Cell 索引
    /// </summary>
    public event Action<int> SelectEvent;
    
    public bool IsDestroyed { get; private set; }

    public int SelectIndex
    {
        get => _selectIndex;
        set
        {
            var newIndex = Mathf.Clamp(value, -1, _cellList.Count - 1);
            if (_selectIndex != newIndex)
            {
                //检测新的 Cell 是否可以被选中
                if (newIndex >= 0)
                {
                    var uiCell = _cellList[newIndex];
                    //不能被选中, 直接跳出
                    if (!uiCell.CanSelect())
                    {
                        return;
                    }
                }

                var prevIndex = _selectIndex;
                _selectIndex = newIndex;

                //取消选中上一个
                if (prevIndex >= 0 && prevIndex < _cellList.Count)
                {
                    var uiCell = _cellList[prevIndex];
                    uiCell.OnUnSelect();
                }

                //选中新的
                if (newIndex >= 0)
                {
                    var uiCell = _cellList[newIndex];
                    uiCell.OnSelect();
                }
                
                if (SelectEvent != null)
                {
                    SelectEvent(newIndex);
                }
            }
        }
    }

    /// <summary>
    /// 选中的 Cell 包含的数据
    /// </summary>
    public TData SelectData => _selectIndex >= 0 ? _cellList[_selectIndex].Data : default;

    public bool Visible
    {
        get => GridContainer.Visible;
        set => GridContainer.Visible = value;
    }

    public int Count => _cellList.Count;

    /// <summary>
    /// Godot 原生网格容器
    /// </summary>
    public GridContainer GridContainer { get; private set; }

    //模板对象
    private TUiCellNode _template;

    //模板大小
    private Vector2 _size = Vector2.Zero;

    //cell逻辑处理类
    private Type _cellType;

    //当前活动的cell池
    private List<UiCell<TUiCellNode, TData>> _cellList = new List<UiCell<TUiCellNode, TData>>();

    //当前已被回收的cell池
    private Stack<UiCell<TUiCellNode, TData>> _cellPool = new Stack<UiCell<TUiCellNode, TData>>();

    //单个cell偏移
    private Vector2I _cellOffset;

    //列数
    private int _columns;

    //是否自动扩展列数
    private bool _autoColumns;

    //选中的cell索引
    private int _selectIndex = -1;

    public UiGrid(TUiCellNode template, Node parent, Type cellType)
    {
        GridContainer = new UiGridContainer(OnReady, OnProcess);
        GridContainer.Ready += OnReady;
        _template = template;
        _cellType = cellType;
        parent.AddChild(GridContainer);
        var uiInstance = _template.GetUiInstance();
        uiInstance.GetParent()?.RemoveChild(uiInstance);
        if (uiInstance is Control control)
        {
            _size = control.Size;
            if (control.CustomMinimumSize == Vector2.Zero)
            {
                control.CustomMinimumSize = _size;
            }
        }
    }
    
    public UiGrid(TUiCellNode template, Type cellType)
    {
        GridContainer = new UiGridContainer(OnReady, OnProcess);
        GridContainer.Ready += OnReady;
        _template = template;
        _cellType = cellType;
        var uiInstance = _template.GetUiInstance();
        uiInstance.AddSibling(GridContainer);
        uiInstance.GetParent().RemoveChild(uiInstance);
        if (uiInstance is Control control)
        {
            _size = control.Size;
            if (control.CustomMinimumSize == Vector2.Zero)
            {
                control.CustomMinimumSize = _size;
            }
        }
    }

    /// <summary>
    /// 设置每个 Cell 之间的偏移量
    /// </summary>
    public void SetCellOffset(Vector2I offset)
    {
        _cellOffset = offset;
        GridContainer.AddThemeConstantOverride("h_separation", offset.X);
        GridContainer.AddThemeConstantOverride("v_separation", offset.Y);
    }

    /// <summary>
    /// 获取每个 Cell 之间的偏移量
    /// </summary>
    public Vector2I GetCellOffset()
    {
        return _cellOffset;
    }

    /// <summary>
    /// 设置列数
    /// </summary>
    public void SetColumns(int columns)
    {
        _columns = columns;
        GridContainer.Columns = columns;
    }

    /// <summary>
    /// 获取列数
    /// </summary>
    public int GetColumns()
    {
        return GridContainer.Columns;
    }

    /// <summary>
    /// 设置是否开启自动扩展列, 如果开启, 则组件会根据 GridContainer 组件所占用的宽度自动设置列数
    /// </summary>
    public void SetAutoColumns(bool flag)
    {
        if (flag != _autoColumns)
        {
            _autoColumns = flag;
            if (_autoColumns)
            {
                GridContainer.Resized += OnGridResized;
                OnGridResized();
            }
            else
            {
                GridContainer.Columns = _columns;
                GridContainer.Resized -= OnGridResized;
            }
        }
    }

    /// <summary>
    /// 获取是否开启自动扩展列
    /// </summary>
    public bool GetAutoColumns()
    {
        return _autoColumns;
    }

    /// <summary>
    /// 设置当前组件布局方式是否横向扩展, 如果为 true, 则 GridContainer 的宽度会撑满父物体
    /// </summary>
    public void SetHorizontalExpand(bool flag)
    {
        SetHorizontalExpand(GridContainer, flag);
    }

    /// <summary>
    /// 获取当前组件布局方式是否横向扩展
    /// </summary>
    public bool GetHorizontalExpand()
    {
        return GetHorizontalExpand(GridContainer);
    }

    /// <summary>
    /// 获取所有数据
    /// </summary>
    public TData[] GetAllData()
    {
        var array = new TData[_cellList.Count];
        for (var i = 0; i < _cellList.Count; i++)
        {
            array[i] = _cellList[i].Data;
        }

        return array;
    }

    /// <summary>
    /// 获取所有 Cell 对象
    /// </summary>
    public UiCell<TUiCellNode, TData>[] GetAllCell()
    {
        return _cellList.ToArray();
    }

    /// <summary>
    /// 根据指定索引获取数据
    /// </summary>
    public TData GetData(int index)
    {
        if (index < 0 || index >= _cellList.Count)
        {
            return default;
        }

        return _cellList[index].Data;
    }

    /// <summary>
    /// 根据指定索引获取 Cell 对象
    /// </summary>
    public UiCell<TUiCellNode, TData> GetCell(int index)
    {
        if (index < 0 || index >= _cellList.Count)
        {
            return default;
        }

        return _cellList[index];
    }
    
    /// <summary>
    /// 根据自定义回调查询数据
    /// </summary>
    public UiCell<TUiCellNode, TData> Find(Func<UiCell<TUiCellNode, TData>, bool> func)
    {
        foreach (var uiCell in _cellList)
        {
            if (func(uiCell))
            {
                return uiCell;
            }
        }

        return null;
    }
    
    /// <summary>
    /// 遍历所有 Cell
    /// </summary>
    public void ForEach(Action<UiCell<TUiCellNode, TData>> callback)
    {
        foreach (var uiCell in _cellList)
        {
            callback(uiCell);
        }
    }
    
    /// <summary>
    /// 遍历所有 Cell, 回调函数返回 false 跳出循环
    /// </summary>
    public void ForEach(Func<UiCell<TUiCellNode, TData>, bool> callback)
    {
        foreach (var uiCell in _cellList)
        {
            if (!callback(uiCell))
            {
                return;
            }
        }
    }

    /// <summary>
    /// 设置当前网格组件中的所有 Cell 数据, 性能较低
    /// </summary>
    public void SetDataList(TData[] array)
    {
        //取消选中
        SelectIndex = -1;
        if (array.Length > _cellList.Count)
        {
            do
            {
                var cell = GetCellInstance();
                GridContainer.AddChild(cell.CellNode.GetUiInstance());
            } while (array.Length > _cellList.Count);
        }
        else if (array.Length < _cellList.Count)
        {
            do
            {
                var cell = _cellList[_cellList.Count - 1];
                _cellList.RemoveAt(_cellList.Count - 1);
                ReclaimCellInstance(cell);
            } while (array.Length < _cellList.Count);
        }

        for (var i = 0; i < _cellList.Count; i++)
        {
            var data = array[i];
            _cellList[i].UpdateData(data);
        }
    }
    
    /// <summary>
    /// 设置当前网格组件中的所有 Cell 数据, 该函数为协程函数，可用于分帧处理大量数据
    /// </summary>
    public IEnumerator SetDataListCoroutine(ICollection<TData> array)
    {
        RemoveAll();
        foreach (var data in array)
        {
            var cell = GetCellInstance();
            GridContainer.AddChild(cell.CellNode.GetUiInstance());
            cell.SetData(data);
            yield return cell.OnSetDataCoroutine(data);
        }
    }

    /// <summary>
    /// 添加单条 Cell 数据, select 为是否立即选中
    /// </summary>
    public void Add(TData data, bool select = false)
    {
        var cell = GetCellInstance();
        GridContainer.AddChild(cell.CellNode.GetUiInstance());
        cell.UpdateData(data);
        if (select)
        {
            SelectIndex = Count - 1;
        }
    }

    /// <summary>
    /// 修改指定索引的位置的 Cell 数据
    /// </summary>
    public void UpdateByIndex(int index, TData data)
    {
        var uiCell = GetCell(index);
        if (uiCell != null)
        {
            uiCell.UpdateData(data);
        }
    }

    /// <summary>
    /// 移除指定索引的 Cell
    /// </summary>
    /// <param name="index"></param>
    public void RemoveByIndex(int index)
    {
        if (index < 0 || index >= _cellList.Count)
        {
            return;
        }

        if (index >= _selectIndex)
        {
            //取消选中
            SelectIndex = -1;
        }

        var uiCell = _cellList[index];
        _cellList.RemoveAt(index);
        ReclaimCellInstance(uiCell);
        //更新后面的索引
        for (var i = index; i < _cellList.Count; i++)
        {
            var tempCell = _cellList[i];
            tempCell.SetIndex(i);
        }
    }

    /// <summary>
    /// 移除所有 Cell
    /// </summary>
    public void RemoveAll()
    {
        //取消选中
        SelectIndex = -1;
        var uiCells = _cellList.ToArray();
        _cellList.Clear();
        foreach (var uiCell in uiCells)
        {
            ReclaimCellInstance(uiCell);
        }
    }

    public void Click(int index)
    {
        if (index < 0 || index >= _cellList.Count)
        {
            return;
        }

        _cellList[index].Click();
    }

    /// <summary>
    /// 对所有已经启用的 Cell 进行排序操作, 排序时会调用 Cell 的 OnSort() 函数用于处理排序逻辑<br/>
    /// 注意: 排序会影响 Cell 的 Index
    /// </summary>
    public void Sort()
    {
        if (_cellList.Count <= 0)
        {
            return;
        }

        //这里记录 SelectIndex 是让排序后 SelectIndex 指向的 Cell 不变
        var selectIndex = SelectIndex;
        var selectCell = GetCell(selectIndex);
        //执行排序操作
        _cellList.Sort((a, b) => a.OnSort(b));
        if (selectIndex >= 0)
        {
            selectIndex = _cellList.FindIndex(cell => cell == selectCell);
        }

        //先移除所有节点
        for (var i = 0; i < _cellList.Count; i++)
        {
            GridContainer.RemoveChild(_cellList[i].CellNode.GetUiInstance());
        }

        if (selectIndex >= 0)
        {
            _selectIndex = selectIndex;
        }

        //以新的顺序加入GridContainer
        for (var i = 0; i < _cellList.Count; i++)
        {
            GridContainer.AddChild(_cellList[i].CellNode.GetUiInstance());
        }

        //刷新Index
        for (var i = 0; i < _cellList.Count; i++)
        {
            var cell = _cellList[i];
            cell.SetIndex(i);
        }
    }

    /// <summary>
    /// 销毁当前网格组件
    /// </summary>
    public void Destroy()
    {
        if (IsDestroyed)
        {
            return;
        }

        IsDestroyed = true;

        for (var i = 0; i < _cellList.Count; i++)
        {
            _cellList[i].Destroy();
        }

        foreach (var uiCell in _cellPool)
        {
            uiCell.Destroy();
        }

        _cellList = null;
        _cellPool = null;
        _template.GetUiInstance().QueueFree();
        GridContainer.QueueFree();
    }

    private void OnReady()
    {
        if (_template.GetUiInstance() is Control control)
        {
            GridContainer.Position = control.Position;
        }
    }

    private void OnProcess(float delta)
    {
        if (IsDestroyed || !_template.GetUiPanel().IsOpen)
        {
            return;
        }

        //调用 cell 更新
        var uiCells = _cellList.ToArray();
        for (var i = 0; i < uiCells.Length; i++)
        {
            var item = uiCells[i];
            if (item.Enable)
            {
                item.Process(delta);
            }
        }
    }

    //获取 cell 实例
    private UiCell<TUiCellNode, TData> GetCellInstance()
    {
        if (_cellPool.Count > 0)
        {
            var cell = _cellPool.Pop();
            cell.SetIndex(_cellList.Count);
            cell.SetEnable(true);
            _cellList.Add(cell);
            return cell;
        }

        var uiCell = Activator.CreateInstance(_cellType) as UiCell<TUiCellNode, TData>;
        if (uiCell is null)
        {
            throw new Exception($"cellType 无法转为'{typeof(UiCell<TUiCellNode, TData>).FullName}'类型!");
        }

        _cellList.Add(uiCell);
        uiCell.Init(this, (TUiCellNode)_template.CloneUiCell(), _cellList.Count - 1);
        uiCell.SetEnable(true);
        return uiCell;
    }

    //回收 cell
    private void ReclaimCellInstance(UiCell<TUiCellNode, TData> cell)
    {
        cell.SetEnable(false);
        GridContainer.RemoveChild(cell.CellNode.GetUiInstance());
        _cellPool.Push(cell);
    }

    private void OnGridResized()
    {
        if (_autoColumns && GridContainer != null)
        {
            var width = GridContainer.Size.X;
            if (width <= _size.X + _cellOffset.X)
            {
                GridContainer.Columns = 1;
            }
            else
            {
                GridContainer.Columns = Mathf.FloorToInt(width / (_size.X + _cellOffset.X));
            }
        }
    }

    /// <summary>
    /// 设置Ui布局方式是否横向扩展, 如果为 true, 则 GridContainer 的宽度会撑满父物体
    /// </summary>
    private static void SetHorizontalExpand(Control control, bool flag)
    {
        if (flag)
        {
            control.LayoutMode = 1;
            control.AnchorsPreset = (int)Control.LayoutPreset.TopWide;
            control.SizeFlagsHorizontal |= Control.SizeFlags.Expand;
        }
        else if ((control.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0)
        {
            control.LayoutMode = 1;
            control.AnchorsPreset = (int)Control.LayoutPreset.TopLeft;
            control.SizeFlagsHorizontal ^= Control.SizeFlags.Expand;
        }
    }

    /// <summary>
    /// 获取Ui布局方式是否横向扩展
    /// </summary>
    private static bool GetHorizontalExpand(Control control)
    {
        return (control.SizeFlagsHorizontal & Control.SizeFlags.Expand) != 0;
    }
}